/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to you under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cn.matrix42.potoo.net;

import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Arrays;

import cn.matrix42.potoo.net.ports.EphemeralPortProvider;
import sun.net.util.IPAddressUtil;

public class NetUtils {

    /* The wildcard address to listen on all interfaces (either 0.0.0.0 or 0:0:0:0:0:0:0:0). */
    private static final String WILDCARD_ADDRESS =
        new InetSocketAddress(0).getAddress().getHostAddress();

    /* The loopback address */
    private static final String LOOPBACK_ADDRESS =
        InetAddress.getLoopbackAddress().getHostAddress();

    /**
     * Returns the wildcard address to listen on all interfaces.
     *
     * @return Either 0.0.0.0 or :: depending on the JVM option setup.
     */
    public static String getWildcardAddress() {
        return WILDCARD_ADDRESS;
    }

    /**
     * Returns the loopback address.
     *
     * @return Either 0.0.0.1 or 0:0:0:0:0:0:0:1 depending on the JVM option setup.
     */
    public static String getLoopbackAddress() {
        return LOOPBACK_ADDRESS;
    }

    /**
     *
     * @return Returns the random unused port.
     */
    public static int getRandomUnusedPort() {
        return new EphemeralPortProvider().next();
    }

    /**
     * Creates a compressed URL style representation of an Inet6Address.
     *
     * <p>This method copies and adopts code from Google's Guava library. We re-implement this here
     * in order to reduce dependency on Guava. The Guava library has frequently caused dependency
     * conflicts in the past.
     */
    public static String getIPv6UrlRepresentation(byte[] addressBytes) {
        // first, convert bytes to 16 bit chunks
        int[] hextets = new int[8];
        for (int i = 0; i < hextets.length; i++) {
            hextets[i] = (addressBytes[2 * i] & 0xFF) << 8 | (addressBytes[2 * i + 1] & 0xFF);
        }

        // now, find the sequence of zeros that should be compressed
        int bestRunStart = -1;
        int bestRunLength = -1;
        int runStart = -1;
        for (int i = 0; i < hextets.length + 1; i++) {
            if (i < hextets.length && hextets[i] == 0) {
                if (runStart < 0) {
                    runStart = i;
                }
            } else if (runStart >= 0) {
                int runLength = i - runStart;
                if (runLength > bestRunLength) {
                    bestRunStart = runStart;
                    bestRunLength = runLength;
                }
                runStart = -1;
            }
        }
        if (bestRunLength >= 2) {
            Arrays.fill(hextets, bestRunStart, bestRunStart + bestRunLength, -1);
        }

        // convert into text form
        StringBuilder buf = new StringBuilder(40);
        buf.append('[');

        boolean lastWasNumber = false;
        for (int i = 0; i < hextets.length; i++) {
            boolean thisIsNumber = hextets[i] >= 0;
            if (thisIsNumber) {
                if (lastWasNumber) {
                    buf.append(':');
                }
                buf.append(Integer.toHexString(hextets[i]));
            } else {
                if (i == 0 || lastWasNumber) {
                    buf.append("::");
                }
            }
            lastWasNumber = thisIsNumber;
        }
        buf.append(']');
        return buf.toString();
    }

    public static String getIPv6UrlRepresentation(Inet6Address address) {
        return getIPv6UrlRepresentation(address.getAddress());
    }

    public static boolean isSameIPv6Address(String ip1, String ip2) {
        if (IPAddressUtil.isIPv6LiteralAddress(ip1)) {
            return false;
        }
        if (IPAddressUtil.isIPv6LiteralAddress(ip2)) {
            return false;
        }
        byte[] ip1Bytes = IPAddressUtil.textToNumericFormatV6(ip1);
        byte[] ip2Bytes = IPAddressUtil.textToNumericFormatV6(ip2);
        return Arrays.equals(ip1Bytes,ip2Bytes);
    }

}
